package regles;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;

import terrain.TypeTerrain;
import unite.TypeCarac;
import unite.TypeCombat;
import unite.TypeUnite;


/**
 * Ensemble des options et des règles du jeu
 * @author Alexis BRASY, Geoffrey LEQUESNE, Sandrine PIRIOU, Michel POPOV
 */
public class Regle implements Serializable {
    private static final long serialVersionUID = 1L;

    /** Mode de combat choisi (combat à mort ou assaut) */
    private TypeCombat modeCombat;
	
    /** Caractéristiques de départ des unités */
    private int[][] carac;
	
    /** Facteur d'impact entre les types d'unité */
    private float[][] facteurImpact;
	
    /** Coût de déplacement par unité sur chaque type de terrain */
    private static int[][] coutDeplac;
	
    /** Mode de scénario choisi */
    private TypeScenario modeScenario;
	
    /** Nombre de potions par joueur au début de la partie */
    private int nbPotion;
	
    /** Nombre de PV rendus à la régénération */
    private int regenPV;
	
    /** Nombre d'XP à partir duquel l'unité est considérée en fin de vie */
    private int xpMortel;
	
    /** Nombre de points de déplacement octroyé en fin de vie */
    private int depPostMortem;
	
    /** Grande constante (utilisée pour le coût de déplacement de cases inaccessibles) */
    private static int GRANDECONSTANTE = 50;
	
    /** Niveau de l'IA */
    private static int nivIA;
	
    /**
     * Constructeur de la classe Regle
     * @param mort mode de combat choisi pour la partie
     * @param modeScenario mode de scénario choisi pour la partie
     * @param nbPotion nombre de potions par joueur au début du jeu
     * @param regenPV nombre de PV rendus par régénération
     * @param xpMortel nombre d'XP à partir duquel l'unité est considérée en fin de vie
     * @param depPostMortem nombre de points de déplacement octroyé en fin de vie
     */
    public Regle(TypeCombat mort, TypeScenario modeScenario, int nbPotion, int regenPV, int xpMortel, int depPostMortem) {
        this.modeCombat = mort;
        this.setModeScenario(modeScenario);
        this.nbPotion = nbPotion;
        this.setRegenPV(regenPV);
        this.setXpMortel(xpMortel);
        this.depPostMortem = depPostMortem;
		
        /* Initialisation du tableau de caractéristiques */
        carac = new int[6][5];
        // Eau
        carac[0][0] = 10; // Att
        carac[0][1] = 10; // Def
        carac[0][2] = 6; // Depl
        carac[0][3] = 2; // Vision
        carac[0][4] = 20; // PV
		
        // Plante
        carac[1][0] = 7; // Att
        carac[1][1] = 13; // Def
        carac[1][2] = 6; // Depl
        carac[1][3] = 2; // Vision
        carac[1][4] = 20; // PV
		
        // Feu
        carac[2][0] = 13; // Att
        carac[2][1] = 7; // Def
        carac[2][2] = 6; // Depl
        carac[2][3] = 2; // Vision
        carac[2][4] = 20; // PV
		
        // Electrique
        carac[3][0] = 10; // Att
        carac[3][1] = 7; // Def
        carac[3][2] = 6; // Depl
        carac[3][3] = 3; // Vision
        carac[3][4] = 15; // PV
		
        // Vol
        carac[4][0] = 7; // Att
        carac[4][1] = 7; // Def
        carac[4][2] = 6; // Depl
        carac[4][3] = 6; // Vision
        carac[4][4] = 15; // PV
		
        // Dresseur
        carac[5][0] = 7; // Att
        carac[5][1] = 10; // Def
        carac[5][2] = 0; // Depl
        carac[5][3] = 3; // Vision
        carac[5][4] = 100; // PV
		
        /* Initialisation du tableau d'influence des types */
        facteurImpact = new float[6][6];
        // Attaquant : Eau
        facteurImpact[0][0] = 0.5f; // Attaqué : Eau
        facteurImpact[0][1] = 0.25f; // Attaqué : Plante
        facteurImpact[0][2] = 0.75f; // Attaqué : Feu
        facteurImpact[0][3] = 0.5f; // Attaqué : Electrique
        facteurImpact[0][4] = 0.5f; // Attaqué : Vol
        facteurImpact[0][5] = 0.5f; // Attaqué : Dresseur
		
        // Attaquant : Plante
        facteurImpact[1][0] = 0.75f; // Attaqué : Eau
        facteurImpact[1][1] = 0.5f; // Attaqué : Plante
        facteurImpact[1][2] = 0.25f; // Attaqué : Feu
        facteurImpact[1][3] = 0.5f; // Attaqué : Electrique
        facteurImpact[1][4] = 0.5f; // Attaqué : Vol
        facteurImpact[1][5] = 0.5f; // Attaqué : Dresseur
		
        // Attaquant : Feu
        facteurImpact[2][0] = 0.25f; // Attaqué : Eau
        facteurImpact[2][1] = 0.75f; // Attaqué : Plante
        facteurImpact[2][2] = 0.5f; // Attaqué : Feu
        facteurImpact[2][3] = 0.5f; // Attaqué : Electrique
        facteurImpact[2][4] = 0.5f; // Attaqué : Vol
        facteurImpact[2][5] = 0.5f; // Attaqué : Dresseur
		
        // Attaquant : Electrique
        facteurImpact[3][0] = 0.5f; // Attaqué : Eau
        facteurImpact[3][1] = 0.5f; // Attaqué : Plante
        facteurImpact[3][2] = 0.5f; // Attaqué : Feu
        facteurImpact[3][3] = 0.5f; // Attaqué : Electrique
        facteurImpact[3][4] = 0.75f; // Attaqué : Vol
        facteurImpact[3][5] = 0.5f; // Attaqué : Dresseur
		
        // Attaquant : Vol
        facteurImpact[4][0] = 0.5f; // Attaqué : Eau
        facteurImpact[4][1] = 0.5f; // Attaqué : Plante
        facteurImpact[4][2] = 0.5f; // Attaqué : Feu
        facteurImpact[4][3] = 0.5f; // Attaqué : Vol
        facteurImpact[4][4] = 0.5f; // Attaqué : Electrique
        facteurImpact[4][5] = 0.5f; // Attaqué : Dresseur
		
        // Attaquant : Dresseur
        facteurImpact[5][0] = 0.5f; // Attaqué : Eau
        facteurImpact[5][1] = 0.5f; // Attaqué : Plante
        facteurImpact[5][2] = 0.5f; // Attaqué : Feu
        facteurImpact[5][3] = 0.5f; // Attaqué : Vol
        facteurImpact[5][4] = 0.5f; // Attaqué : Electrique
        facteurImpact[5][5] = 0.5f; // Attaqué : Dresseur
		
        /* Initialisation des coûts de déplacement */
        coutDeplac = new int[6][5];
		
        // Type : Mer
        coutDeplac[0][0] = 1; // Eau
        coutDeplac[0][1] = GRANDECONSTANTE; // Plante
        coutDeplac[0][2] = GRANDECONSTANTE; // Feu
        coutDeplac[0][3] = GRANDECONSTANTE; // Electrique
        coutDeplac[0][4] = 1; // Vol
		
        // Type : Plaine
        coutDeplac[1][0] = 1; // Eau
        coutDeplac[1][1] = 1; // Plante
        coutDeplac[1][2] = 1; // Feu
        coutDeplac[1][3] = 1; // Electrique
        coutDeplac[1][4] = 1; // Vol
		
        // Type : Forêt
        coutDeplac[2][0] = 2; // Eau
        coutDeplac[2][1] = 1; // Plante
        coutDeplac[2][2] = 2; // Feu
        coutDeplac[2][3] = 2; // Electrique
        coutDeplac[2][4] = 1; // Vol
		
        // Type : Sable
        coutDeplac[3][0] = 2; // Eau
        coutDeplac[3][1] = 2; // Plante
        coutDeplac[3][2] = 1; // Feu
        coutDeplac[3][3] = 2; // Electrique
        coutDeplac[3][4] = 1; // Vol
		
        // Type : Montagne
        coutDeplac[4][0] = 3; // Eau
        coutDeplac[4][1] = 3; // Plante
        coutDeplac[4][2] = 3; // Feu
        coutDeplac[4][3] = 3; // Electrique
        coutDeplac[4][4] = 1; // Vol
		
        // Type : Glace
        coutDeplac[5][0] = 0; // Eau
        coutDeplac[5][1] = 0; // Plante
        coutDeplac[5][2] = 0; // Feu
        coutDeplac[5][3] = 0; // Electrique
        coutDeplac[5][4] = 1; // Vol
    }

    /**
     * Récupération du facteur d'impact entre deux types d'unité 
     * @param attaquant l'unité attaquante
     * @param attaque l'unité Attaquée
     * @return le facteur d'impact entre attaquant et Attaqué
     */
    public float getFacteurImpact(TypeUnite attaquant, TypeUnite attaque) {
        int ligne = -1, colonne = -1; 

        switch (attaquant) {
        case EAU:
            ligne = 0;
            break;

        case PLANTE: 
            ligne = 1;
            break;

        case FEU:
            ligne = 2;
            break;

        case ELECTRIQUE:
            ligne = 3;
            break;

        case VOL:
            ligne = 4;
            break;

        case DRESSEUR:
            ligne = 5;
            break;

        default:
            ligne = -1;
        }
		
        switch (attaque) {
        case EAU:
            colonne = 0;
            break;

        case PLANTE: 
            colonne = 1;
            break;

        case FEU:
            colonne = 2;
            break;

        case ELECTRIQUE:
            colonne = 3;
            break;

        case VOL:
            colonne = 4;
            break;

        case DRESSEUR:
            colonne = 5;
            break;

        default:
            colonne = -1;
        }
		
        if (ligne != -1 && colonne != -1) {
            return facteurImpact[ligne][colonne];
        } else { 
            return 0.0f;
        }
    }
	
    /**
     * Accesseur d'une caractéristique
     * @param unite type d'unité
     * @param carac type de caractéristique
     * @return la caractéristique demandée
     */
    public int getCaracUnite(TypeUnite unite, TypeCarac carac) {
        int ligne = -1, colonne = -1;
		
        switch (unite) {
        case EAU:
            ligne = 0;
            break;

        case PLANTE: 
            ligne = 1;
            break;

        case FEU:
            ligne = 2;
            break;

        case ELECTRIQUE:
            ligne = 3;
            break;

        case VOL:
            ligne = 4;
            break;

        case DRESSEUR:
            ligne = 5;
            break;

        default:
            ligne = -1;
        }
		
        switch (carac) {
        case ATTAQUE:
            colonne = 0;
            break;

        case DEFENSE:
            colonne = 1;
            break;

        case DEPLACEMENT:
            colonne = 2;
            break;

        case VISION:
            colonne = 3;
            break;

        case PV:
            colonne = 4;
            break;

        default:
            colonne = -1;
        }
		
        if (ligne != -1 && colonne != -1) {
            return this.carac[ligne][colonne];
        } else {
            return -1;
        }
    }
	
    /**
     * Accesseur d'un coût de déplacement d'une case en fonction de son type et de l'unité
     * @param terrain type de terrain
     * @param unite type d'unité
     * @return le coût de déplacement de cette unité sur ce terrain
     */
    public int getCoutDepl(TypeTerrain terrain, TypeUnite unite) {
        int ligne = -1, colonne = -1;
		
        switch (terrain) {
        case MER:
            ligne = 0;
            break;

        case PLAINE:
            ligne = 1;
            break;

        case FORET:
            ligne = 2;
            break;

        case SABLE:
            ligne = 3;
            break;

        case MONTAGNE:
            ligne = 4;
            break;

        case GLACE:
            ligne = 5;
            break;

        case VIDE:
            return GRANDECONSTANTE;

        default:
            ligne = -1;
        }
		
        switch (unite) {
        case EAU:
            colonne = 0;
            break;

        case PLANTE:
            colonne = 1;
            break;

        case FEU:
            colonne = 2;
            break;

        case ELECTRIQUE:
            colonne = 3;
            break;

        case VOL:
            colonne = 4;
            break;

        default:
            colonne = -1;
        }
		
        if (ligne != -1 && colonne != -1) {
            return coutDeplac[ligne][colonne];
        } else {
            return -1;
        }
    }
	
    /**
     * Acesseur de l'attribut depPostMortem
     * @return le nombre de points de déplacement final de l'unité
     */
    public int getDepPostMortem() {
        return depPostMortem;
    }
	
    public void setDepPostMortem(int mort) {
        this.depPostMortem = mort;
    }
	
    /**
     * Accesseur de l'attribut nbPotion
     * @return le nombre de potions possédées par équipe au début du jeu
     */
    public int getNbPotion() {
        return nbPotion;
    }
	
    public void setNbPotion(int a) {
        this.nbPotion = a;
    }

    public TypeCombat getModeCombat() {
        return modeCombat;
    }
	
    public void setModeCombat(TypeCombat modeCombat1) {
        this.modeCombat = modeCombat1;
    }

    public void setModeScenario(TypeScenario modeScenario) {
        this.modeScenario = modeScenario;
    }

    public TypeScenario getModeScenario() {
        return modeScenario;
    }

    public void setRegenPV(int regenPV) {
        this.regenPV = regenPV;
    }

    public int getRegenPV() {
        return regenPV;
    }

    public void setXpMortel(int xpMortel) {
        this.xpMortel = xpMortel;
    }

    public int getXpMortel() {
        return xpMortel;
    }

    public static void setGRANDECONSTANTE(int gRANDECONSTANTE) {
        GRANDECONSTANTE = gRANDECONSTANTE;
    }

    public static int getGRANDECONSTANTE() {
        return GRANDECONSTANTE;
    }
	
    public Collection<Integer> getPaliersEvolution() {
        Collection<Integer> c = new ArrayList<Integer>();

        c.add(50);
        c.add(150);
        c.add(300);
        return c;
    }

    public void setNivIA(int nivIA) {
        Regle.nivIA = nivIA;
    }

    public int getNivIA() {
        return nivIA;
    }
}
